import torch
import numpy as np
import pandas as pd
import torch.nn as nn  # 这里将重要的库命名。
import torch.nn.functional as F  # 这里将内置函数也命名。

torch.manual_seed(2233)  # 这里是设置一个随机种子。


class Network(nn.Module):  # 这里要将其变成副类。
    def __init__(self):
        super(Network, self).__init__()  # 这里表示继承类里面
        self.liner_1 = nn.Linear(2, 10)  # 这里表示的是有两个特征，隐藏层有10个。
        self.liner_2 = nn.Linear(10, 10)  # 这里是输入下一层。
        self.liner_3 = nn.Linear(10, 1)  # 这里使用sigmoid函数作为输出。

    def forward(self, input_data):  # 这里的input表示的是你要输入的那个数据，同时这里也是向前传播。
        z = self.liner_1(input_data)  # 这里表示输入层转化为线性函数。
        z = F.relu(z)  # 这里表示用relu函数将z激活。
        z = self.liner_2(z)  # 这里再转化为线性函数。
        z = F.relu(z)  # 再一次使用费relu函数将其激活。
        z = self.liner_3(z)  # 再一次转化为线性函数
        z = torch.sigmoid(z)  # 最后使用的是sigmoid函数来激活。
        z = z.squeeze(-1)
        return z  # 将最终的结果也就是分类结果进行返回。


def get_it():  # 这里是获得模型的函数。
    two_class = Network()  # 首先还是实例化。
    opt = torch.optim.Adam(two_class.parameters(), lr=0.015)  # 这里设置学习率和优化方法。
    return two_class, opt


data = pd.read_csv("./data/train.csv", header=None)

y_data = data.iloc[2, :]
x_data = data.iloc[0:2, :]  # 提取出数据。
x1 = np.array(x_data).T  # 转化为列向量。
y1 = np.array(y_data)  # 转化为矩阵的形式
# print(y1.shape)
# print(x1.shape)

# 这里将数据转化为张量
x = torch.from_numpy(x1)  # 直接转化为张  量。
y = torch.from_numpy(y1)
y = y.float()
x = x.float()
# print(x.dtype)

loss_function = torch.nn.BCELoss()
net, opti = get_it()  # 这里是将变量实例化还有获得训练的方法。
print("这里是训练集用于训练网络的损失值以及准确率：")
for i in range(101):
    finish = net(x)  # 这里是将数据传进去。
    # print(finish)
    # print(finish.dtype)
    loss = loss_function(finish, y)  # 这里是将返回的结果传进去，然后和真实值做对比。
    # print(loss)
    if i % 10 == 0:  # 这里是定义的一个if语句，用于输出中间值。

        # 这里是计算准确率的。
        middle_data = torch.empty_like(finish)  # 创造一个空张量,大小是和finish一样大的。。
        middle_data = middle_data.copy_(finish)  # 这里将数据打印，变量不在一个内存里面。

        zero = torch.zeros_like(loss)
        one = torch.ones_like(loss)  # 创造一个0张量和1张量。

        middle_data = torch.where(middle_data >= 0.5, one, middle_data)  # 这里将大等于0.5的变量转化为1
        middle_data = torch.where(middle_data < 0.5, zero, middle_data)  # 这里将小于0.5的变量转化为0.

        acc = 100 * (middle_data == y).sum() / y.numel()  # 这里表示的是

        print("迭代次数为：" + str(i) + ".  此时的损失值为：" + str(float(loss)) + ".  此时模型的准确率是：" + str(
            float(acc)) + "%")
        # 接下来就是计算准确率。

    opti.zero_grad()  # 将权重变成零。
    loss.backward()  # 这里将损失值进行反向传播
    opti.step()  # 更新新的梯度。
# 接下来是导入测试集的数据并进行划分。
test_data = pd.read_csv("./data/test.csv", header=None)
test_x = test_data.iloc[0:2, :]
test_y = test_data.iloc[2, :]
test_x = torch.from_numpy(np.array(test_x).T)
test_y = torch.from_numpy(np.array(test_y))
test_x = test_x.float()
test_y = test_y.float()

finish2 = net(test_x)

zero1 = torch.zeros_like(finish2)
one1 = torch.ones_like(finish2)  # 创造一个0张量和1张量。
finish2 = torch.where(finish2 >= 0.5, one1, finish2)  # 这里将大等于0.5的变量转化为1
finish2 = torch.where(finish2 < 0.5, zero1, finish2)  # 这里将小于0.5的变量转化为0.
acc = 100 * (finish2 == test_y).sum() / test_y.numel()  # 这里表示的是
loss1 = loss_function(finish2, test_y)
# print(test_y)
# print(finish2)
print("测试集模型的准确率是：" + str(float(acc)) + "%")
